convert xcsv format to sub-class of Format. (#481)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Thu, 30 Jan 2020 18:06:25 +0000 (11:06 -0700)
committerGitHub <noreply@github.com>
Thu, 30 Jan 2020 18:06:25 +0000 (11:06 -0700)
* convert xcsv format to sub-class of Format.

* move XcsvStyle functions inside class.

* Unnest XcsvStyle.

* remove redundant qualifiers.

vecs.cc
vecs.h
xcsv.cc
xcsv.h

diff --git a/vecs.cc b/vecs.cc
index 500ebb80d6d7d183042f0c712645d90fa6d0e19b..9878279f2b20aa69f83b4973541f096a5cd4c9c0 100644 (file)
--- a/vecs.cc
+++ b/vecs.cc
@@ -269,7 +269,7 @@ Format* Vecs::find_vec(const QString& vecname)
      * format that utilized an internal style file, then we need to let
      * xcsv know the internal style file is no longer in play.
      */
-     xcsv_setup_internal_style(nullptr);
+     xcsv_fmt.xcsv_setup_internal_style(nullptr);
 #endif // CSVFMTS_ENABLED
     vec.vec->set_name(vec.name);       /* needed for session information */
     return vec.vec;
@@ -315,7 +315,7 @@ Format* Vecs::find_vec(const QString& vecname)
       disp_vec_options(svec.name, xcsv_args);
     }
 #if CSVFMTS_ENABLED
-    xcsv_setup_internal_style(svec.style_buf);
+    xcsv_fmt.xcsv_setup_internal_style(svec.style_buf);
 #endif // CSVFMTS_ENABLED
 
     vec_list[0].vec->set_name(svec.name);      /* needed for session information */
@@ -404,7 +404,7 @@ QVector<Vecs::vecinfo_t> Vecs::sort_and_unify_vecs() const
 
   /* Gather the relevant info for the style based formats. */
   for (const auto& svec : style_list) {
-    XcsvStyle style = xcsv_read_internal_style(svec.style_buf);
+    XcsvStyle style = XcsvStyle::xcsv_read_internal_style(svec.style_buf);
     vecinfo_t info;
     info.name = svec.name;
     info.desc = style.description;
@@ -431,9 +431,9 @@ QVector<Vecs::vecinfo_t> Vecs::sort_and_unify_vecs() const
      * 'Full path to XCSV style file' argument to any
      * GUIs for an internal format.
      */
-    bool first = true;
     const QVector<arglist_t>* args = vec_list.at(0).vec->get_args();
     if (args != nullptr) {
+      bool first = true;
       for (const auto& arg : *args) {
         if (!first) {
           info.arginfo.append(arginfo_t(arg));
@@ -673,7 +673,7 @@ bool Vecs::validate_args(const QString& name, const QVector<arglist_t>* args)
   return ok;
 }
 
-bool Vecs::validate_vec(const Vecs::vecs_t& vec)
+bool Vecs::validate_vec(const vecs_t& vec)
 {
   bool ok = validate_args(vec.name, vec.vec->get_args());
 
diff --git a/vecs.h b/vecs.h
index 2d2870a6a2a305b9cd0d07dd3bf77ac8728f9670..e97a2f5931109fb53a2f9629a912693ef757eb07 100644 (file)
--- a/vecs.h
+++ b/vecs.h
 #include "gpx.h"
 #include "legacyformat.h"
 #include "mynav.h"
+#include "xcsv.h"
 
 
-#if CSVFMTS_ENABLED
-extern ff_vecs_t xcsv_vecs;
-#endif // CSVFMTS_ENABLED
 extern ff_vecs_t geo_vecs;
 extern ff_vecs_t mag_svecs;
 extern ff_vecs_t mag_fvecs;
@@ -268,7 +266,7 @@ private:
    * of this class is constructed.
    */
 #if CSVFMTS_ENABLED
-  LegacyFormat xcsv_fmt {xcsv_vecs};
+  XcsvFormat xcsv_fmt;
 #endif // CSVFMTS_ENABLED
   LegacyFormat geo_fmt {geo_vecs};
   GpxFormat gpx_fmt;
diff --git a/xcsv.cc b/xcsv.cc
index f2d87c3a5194782783b7d65cf08e2ee2d50db609..d61d9f73a08f4287a3d36a4551a300dd986e9ef7 100644 (file)
--- a/xcsv.cc
+++ b/xcsv.cc
@@ -44,7 +44,6 @@
 #include <QtCore/QStringList>      // for QStringList
 #include <QtCore/QTextStream>      // for QTextStream
 #include <QtCore/QTime>            // for QTime
-#include <QtCore/QVector>          // for QVector
 #include <QtCore/QtGlobal>         // for qAsConst, QAddConst<>::Type, qPrintable
 
 #include "defs.h"
 #include "strptime.h"              // for strptime
 #include "xcsv.h"
 
-#define MYNAME "XCSV"
-
-/* macros */
-constexpr char lat_dir(double a) {return a < 0.0 ? 'S' : 'N';}
-constexpr char lon_dir(double a) {return a < 0.0 ? 'W' : 'E';}
 
-/* convert excel time (days since 1900) to time_t and back again */
-constexpr double excel_to_timet(double a) {return (a - 25569.0) * 86400.0;}
-constexpr double timet_to_excel(double a) {return (a / 86400.0) + 25569.0;}
+#if CSVFMTS_ENABLED
 
-constexpr int gps_datum_wgs84 = 118; // GPS_Lookup_Datum_Index("WGS 84")
+#define MYNAME "XCSV"
 
 /*
  * Internal numeric value to associate with each keyword in a style file.
@@ -173,91 +165,8 @@ enum xcsv_token {
 
 #include "xcsv_tokens.gperf"       // for Perfect_Hash, xt_mapping
 
-#if CSVFMTS_ENABLED
-/****************************************************************************/
-/* obligatory global struct                                                 */
-/****************************************************************************/
-
-static XcsvFile* xcsv_file;
-static const XcsvStyle* xcsv_style;
-static double pathdist = 0;
-static double oldlon = 999;
-static double oldlat = 999;
-
-static int waypt_out_count = 0;
-static const route_head* csv_track = nullptr;
-static const route_head* csv_route = nullptr;
-
-struct xcsv_parse_data {
-  QString rte_name;
-  QString trk_name;
-  bool new_track{false};
-  double utm_northing{0};
-  double utm_easting{0};
-  double utm_zone{0};
-  char utm_zonec{'N'};
-  UrlLink* link_{nullptr};
-  gpsbabel_optional::optional<bool> lat_dir_positive;
-  gpsbabel_optional::optional<bool> lon_dir_positive;
-};
-
-static char* styleopt = nullptr;
-static char* snlenopt = nullptr;
-static char* snwhiteopt = nullptr;
-static char* snupperopt = nullptr;
-static char* snuniqueopt = nullptr;
-static char* prefer_shortnames = nullptr;
-static char* xcsv_urlbase = nullptr;
-static char* opt_datum = nullptr;;
-
-static const char* intstylebuf = nullptr;
-
-static
-QVector<arglist_t> xcsv_args = {
-  {
-    "style", &styleopt, "Full path to XCSV style file", nullptr,
-    ARGTYPE_FILE | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
-  },
-  {
-    "snlen", &snlenopt, "Max synthesized shortname length", nullptr,
-    ARGTYPE_INT, "1", nullptr, nullptr
-  },
-  {
-    "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames",
-    nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
-  },
-  {
-    "snupper", &snupperopt, "UPPERCASE synth. shortnames",
-    nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
-  },
-  {
-    "snunique", &snuniqueopt, "Make synth. shortnames unique",
-    nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
-  },
-  {
-    "urlbase", &xcsv_urlbase, "Basename prepended to URL on output",
-    nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
-  },
-  {
-    "prefer_shortnames", &prefer_shortnames,
-    "Use shortname instead of description",
-    nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
-  },
-  {
-    "datum", &opt_datum, "GPS datum (def. WGS 84)",
-    nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
-  },
-};
-
-/* something to map config file constants to chars */
-struct char_map_t {
-  const QString key;
-  const QString chars;
-};
-
 /* a table of config file constants mapped to chars */
-static
-char_map_t xcsv_char_table[] = {
+const XcsvStyle::char_map_t XcsvStyle::xcsv_char_table[] = {
   { "COMMA",           ","     },
   { "COMMASPACE",              ", "    },
   { "SINGLEQUOTE",     "'"     },
@@ -276,12 +185,12 @@ char_map_t xcsv_char_table[] = {
 };
 
 // Given a keyword of "COMMASPACE", return ", ".
-static QString
-xcsv_get_char_from_constant_table(const QString& key)
+QString
+XcsvStyle::xcsv_get_char_from_constant_table(const QString& key)
 {
   static QHash<QString, QString> substitutions;
   if (substitutions.empty()) {
-    for (char_map_t* cm = xcsv_char_table; !cm->key.isNull(); cm++) {
+    for (const char_map_t* cm = xcsv_char_table; !cm->key.isNull(); cm++) {
       substitutions.insert(cm->key, cm->chars);
     }
   }
@@ -294,14 +203,14 @@ xcsv_get_char_from_constant_table(const QString& key)
 
 // Remove outer quotes.
 // Should probably be in csv_util.
-static QString dequote(const QString& in) {
+QString XcsvStyle::dequote(const QString& in) {
   QString r = in.simplified();
   if (r.startsWith("\"")) r = r.mid(1);
   if (r.endsWith("\"")) r.chop(1);
   return r;
 }
 
-static void validate_fieldmap(const field_map& fmp, bool is_output) {
+void XcsvStyle::validate_fieldmap(const field_map& fmp, bool is_output) {
   if (fmp.key.isEmpty()) {
     Fatal() << MYNAME << ": xcsv style is missing" <<
             (is_output ? "output" : "input") << "field type.";
@@ -318,8 +227,8 @@ static void validate_fieldmap(const field_map& fmp, bool is_output) {
 /* xcsv_ifield_add() - add input field to ifield queue.                      */
 /* usage: xcsv_ifield_add("DESCRIPTION", "", "%s")                           */
 /*****************************************************************************/
-static void
-xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc)
+void
+XcsvStyle::xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc)
 {
   QByteArray key = qkey.toUtf8();
   QByteArray val = qval.toUtf8();
@@ -337,8 +246,8 @@ xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, cons
 /* xcsv_ofield_add() - add output field to ofield queue.                     */
 /* usage: xcsv_ofield_add("LAT_DECIMAL", "", "%08.5lf")                      */
 /*****************************************************************************/
-static void
-xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned options)
+void
+XcsvStyle::xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned options)
 {
   QByteArray key = qkey.toUtf8();
   QByteArray val = qval.toUtf8();
@@ -352,8 +261,8 @@ xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, cons
   style->ofields.append(fmp);
 }
 
-static QDateTime
-yyyymmdd_to_time(const char* s)
+QDateTime
+XcsvFormat::yyyymmdd_to_time(const char* s)
 {
   QDate d = QDate::fromString(s, "yyyyMMdd");
   return QDateTime(d);
@@ -363,8 +272,8 @@ yyyymmdd_to_time(const char* s)
 /*
  * sscanftime - Parse a date buffer using strftime format
  */
-static time_t
-sscanftime(const char* s, const char* format, const int gmt)
+time_t
+XcsvFormat::sscanftime(const char* s, const char* format, const int gmt)
 {
   struct tm stm;
   memset(&stm, 0, sizeof(stm));
@@ -390,8 +299,8 @@ sscanftime(const char* s, const char* format, const int gmt)
   return 0;
 }
 
-static time_t
-addhms(const char* s, const char* format)
+time_t
+XcsvFormat::addhms(const char* s, const char* format)
 {
   time_t tt = 0;
   int hour = 0;
@@ -412,8 +321,8 @@ addhms(const char* s, const char* format)
   return tt;
 }
 
-static QString
-writetime(const char* format, time_t t, bool gmt)
+QString
+XcsvFormat::writetime(const char* format, time_t t, bool gmt)
 {
   static struct tm* stmp;
 
@@ -432,14 +341,14 @@ writetime(const char* format, time_t t, bool gmt)
   return QString(tbuff);
 }
 
-static QString
-writetime(const char* format, const gpsbabel::DateTime& t, bool gmt)
+QString
+XcsvFormat::writetime(const char* format, const gpsbabel::DateTime& t, bool gmt)
 {
   return writetime(format, t.toTime_t(), gmt);
 }
 
-static QString
-writehms(const char* format, time_t t, int gmt)
+QString
+XcsvFormat::writehms(const char* format, time_t t, int gmt)
 {
   static struct tm no_time = tm();
   static struct tm* stmp = &no_time;
@@ -459,21 +368,21 @@ writehms(const char* format, time_t t, int gmt)
                             (stmp->tm_hour >= 12 ? "PM" : "AM"));
 }
 
-static QString
-writehms(const char* format, const gpsbabel::DateTime& t, int gmt)
+QString
+XcsvFormat::writehms(const char* format, const gpsbabel::DateTime& t, int gmt)
 {
   return writehms(format, t.toTime_t(), gmt);
 }
 
-static long
-time_to_yyyymmdd(const QDateTime& t)
+long
+XcsvFormat::time_to_yyyymmdd(const QDateTime& t)
 {
   QDate d = t.date();
   return d.year() * 10000 + d.month() * 100 + d.day();
 }
 
-static garmin_fs_t*
-gmsd_init(Waypoint* wpt)
+garmin_fs_t*
+XcsvFormat::gmsd_init(Waypoint* wpt)
 {
   garmin_fs_t* gmsd = garmin_fs_t::find(wpt);
   if (gmsd == nullptr) {
@@ -487,8 +396,8 @@ gmsd_init(Waypoint* wpt)
 /* xcsv_parse_val() - parse incoming data into the waypt structure.          */
 /* usage: xcsv_parse_val("-123.34", *waypt, *field_map)                      */
 /*****************************************************************************/
-static void 
-xcsv_parse_val(const QString& value, Waypoint* wpt, const field_map& fmp,
+void 
+XcsvFormat::xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle::field_map& fmp,
                xcsv_parse_data* parse_data, const int line_no)
 {
   const char* enclosure = "";
@@ -913,11 +822,11 @@ xcsv_parse_val(const QString& value, Waypoint* wpt, const field_map& fmp,
 }
 
 /*****************************************************************************/
-/* xcsv_data_read() - read input file, parsing lines, fields and handling    */
+/* read() - read input file, parsing lines, fields and handling              */
 /*                   any data conversion (the input meat)                    */
 /*****************************************************************************/
-static void
-xcsv_data_read()
+void
+XcsvFormat::read()
 {
   int linecount = 0;
   route_head* rte = nullptr;
@@ -967,7 +876,7 @@ xcsv_data_read()
 
       /* now rip the line apart */
       for (const auto& value : values) {
-        const field_map& fmp = xcsv_style->ifields.at(ifield_idx++);
+        const XcsvStyle::field_map& fmp = xcsv_style->ifields.at(ifield_idx++);
         xcsv_parse_val(value, wpt_tmp, fmp, &parse_data, linecount);
 
         if (ifield_idx >= xcsv_style->ifields.size()) {
@@ -1037,8 +946,8 @@ xcsv_data_read()
   }
 }
 
-static void
-xcsv_resetpathlen(const route_head* head)
+void
+XcsvFormat::xcsv_resetpathlen(const route_head* head)
 {
   pathdist = 0;
   oldlat = 999;
@@ -1060,8 +969,8 @@ xcsv_resetpathlen(const route_head* head)
 /* xcsv_waypt_pr() - write output file, handling output conversions          */
 /*                  (the output meat)                                        */
 /*****************************************************************************/
-static void
-xcsv_waypt_pr(const Waypoint* wpt)
+void
+XcsvFormat::xcsv_waypt_pr(const Waypoint* wpt)
 {
   QString buff;
   double latitude, longitude;
@@ -1132,11 +1041,11 @@ xcsv_waypt_pr(const Waypoint* wpt)
      */
     int field_is_unknown = 0;
 
-    if ((i != 0) && !(fmp.options & options_nodelim)) {
+    if ((i != 0) && !(fmp.options & XcsvStyle::options_nodelim)) {
       xcsv_file->stream << write_delimiter;
     }
 
-    if (fmp.options & options_absolute) {
+    if (fmp.options & XcsvStyle::options_absolute) {
       lat = fabs(lat);
       lon = fabs(lon);
     }
@@ -1152,7 +1061,7 @@ xcsv_waypt_pr(const Waypoint* wpt)
       buff = QString::asprintf(fmp.printfc.constData(), waypt_out_count + atoi(fmp.val.constData()));
       break;
     case XT_CONSTANT: {
-      auto cp = xcsv_get_char_from_constant_table(fmp.val.constData());
+      auto cp = XcsvStyle::xcsv_get_char_from_constant_table(fmp.val.constData());
       if (!cp.isEmpty()) {
         buff = QString::asprintf(fmp.printfc.constData(), CSTR(cp));
       } else {
@@ -1642,7 +1551,7 @@ xcsv_waypt_pr(const Waypoint* wpt)
     }
     QString obuff = csv_stringclean(buff, xcsv_style->badchars);
 
-    if (field_is_unknown && fmp.options & options_optional) {
+    if (field_is_unknown && fmp.options & XcsvStyle::options_optional) {
       continue;
     }
 
@@ -1673,8 +1582,8 @@ xcsv_waypt_pr(const Waypoint* wpt)
 }
 
 // return |original| after performing token replacement.
-static QString
-xcsv_replace_tokens(const QString& original) {
+QString
+XcsvFormat::xcsv_replace_tokens(const QString& original) const {
   QString replacement = original;
     // Don't do potentially expensive replacements if token prefix
     // isn't present;
@@ -1697,11 +1606,11 @@ xcsv_replace_tokens(const QString& original) {
 }
 
 /*****************************************************************************/
-/* xcsv_data_write(void) - write prologues, spawn the output loop, and write */
+/* write(void) - write prologues, spawn the output loop, and write           */
 /*                         epilogues.                                        */
 /*****************************************************************************/
-static void
-xcsv_data_write()
+void
+XcsvFormat::write()
 {
   /* reset the index counter */
   waypt_out_count = 0;
@@ -1712,14 +1621,21 @@ xcsv_data_write()
     xcsv_file->stream << line_to_write <<  xcsv_style->record_delimiter;
   }
 
+  auto xcsv_waypt_pr_lambda = [this](const Waypoint* wpt)->void {
+    xcsv_waypt_pr(wpt);
+  };
+  auto xcsv_resetpathlen_lambda = [this](const route_head* rte)->void {
+    xcsv_resetpathlen(rte);
+  };
+
   if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == wptdata)) {
-    waypt_disp_all(xcsv_waypt_pr);
+    waypt_disp_all(xcsv_waypt_pr_lambda);
   }
   if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == rtedata)) {
-    route_disp_all(xcsv_resetpathlen, nullptr, xcsv_waypt_pr);
+    route_disp_all(xcsv_resetpathlen_lambda, nullptr, xcsv_waypt_pr_lambda);
   }
   if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == trkdata)) {
-    track_disp_all(xcsv_resetpathlen, nullptr, xcsv_waypt_pr);
+    track_disp_all(xcsv_resetpathlen_lambda, nullptr, xcsv_waypt_pr_lambda);
   }
 
   /* output epilogue lines, if any. */
@@ -1729,8 +1645,8 @@ xcsv_data_write()
   }
 }
 
-static void
-xcsv_parse_style_line(XcsvStyle* style, QString line)
+void
+XcsvStyle::xcsv_parse_style_line(XcsvStyle* style, QString line)
 {
   // The lines to be parsed have a leading operation |op| that is
   // separated by whitespace from the rest. Each op may have zero or
@@ -1906,8 +1822,8 @@ xcsv_parse_style_line(XcsvStyle* style, QString line)
  * a terminating null.   Makes multiple calls to that function so
  * that "ignore to end of line" comments work right.
  */
-static XcsvStyle
-xcsv_parse_style_buff(const char* sbuff)
+XcsvStyle
+XcsvStyle::xcsv_parse_style_buff(const char* sbuff)
 {
   XcsvStyle style;
   const QStringList lines = QString(sbuff).split('\n');
@@ -1917,8 +1833,8 @@ xcsv_parse_style_buff(const char* sbuff)
   return style;
 }
 
-static XcsvStyle
-xcsv_read_style(const char* fname)
+XcsvStyle
+XcsvStyle::xcsv_read_style(const char* fname)
 {
   gbfile* fp = gbfopen(fname, "rb", MYNAME);
   XcsvStyle style;
@@ -1942,7 +1858,7 @@ xcsv_read_style(const char* fname)
  * the xcsv parser and make it ready for general use.
  */
 XcsvStyle
-xcsv_read_internal_style(const char* style_buf)
+XcsvStyle::xcsv_read_internal_style(const char* style_buf)
 {
   XcsvStyle style = xcsv_parse_style_buff(style_buf);
 
@@ -1955,26 +1871,26 @@ xcsv_read_internal_style(const char* style_buf)
 }
 
 void
-xcsv_setup_internal_style(const char* style_buf)
+XcsvFormat::xcsv_setup_internal_style(const char* style_buf)
 {
   intstylebuf = style_buf;
 }
 
-static void
-xcsv_rd_init(const QString& fname)
+void
+XcsvFormat::rd_init(const QString& fname)
 {
   /*
    * if we don't have an internal style defined, we need to
    * read it from a user-supplied style file, or die trying.
    */
   if (intstylebuf != nullptr) {
-    xcsv_style = new XcsvStyle(xcsv_read_internal_style(intstylebuf));
+    xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_internal_style(intstylebuf));
   } else {
     if (!styleopt) {
       fatal(MYNAME ": XCSV input style not declared.  Use ... -i xcsv,style=path/to/file.style\n");
     }
 
-    xcsv_style = new XcsvStyle(xcsv_read_style(styleopt));
+    xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt));
   }
 
   if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == wptdata)) {
@@ -2004,8 +1920,8 @@ xcsv_rd_init(const QString& fname)
   assert(gps_datum_wgs84 == GPS_Lookup_Datum_Index("WGS 84"));
 }
 
-static void
-xcsv_rd_deinit()
+void
+XcsvFormat::rd_deinit()
 {
   xcsv_file->stream.close();
   delete xcsv_file;
@@ -2015,21 +1931,21 @@ xcsv_rd_deinit()
   xcsv_style = nullptr;
 }
 
-static void
-xcsv_wr_init(const QString& fname)
+void
+XcsvFormat::wr_init(const QString& fname)
 {
   /*
    * if we don't have an internal style defined, we need to
    * read it from a user-supplied style file, or die trying.
    */
   if (intstylebuf != nullptr) {
-    xcsv_style = new XcsvStyle(xcsv_read_internal_style(intstylebuf));
+    xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_internal_style(intstylebuf));
   } else {
     if (!styleopt) {
       fatal(MYNAME ": XCSV output style not declared.  Use ... -o xcsv,style=path/to/file.style\n");
     }
 
-    xcsv_style = new XcsvStyle(xcsv_read_style(styleopt));
+    xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt));
   }
 
   xcsv_file = new XcsvFile;
@@ -2083,14 +1999,14 @@ xcsv_wr_init(const QString& fname)
   assert(gps_datum_wgs84 == GPS_Lookup_Datum_Index("WGS 84"));
 }
 
-static void
-xcsv_wr_position_init(const QString& fname)
+void
+XcsvFormat::wr_position_init(const QString& fname)
 {
-  xcsv_wr_init(fname);
+  wr_init(fname);
 }
 
-static void
-xcsv_wr_deinit()
+void
+XcsvFormat::wr_deinit()
 {
   xcsv_file->stream.close();
   delete xcsv_file;
@@ -2100,14 +2016,14 @@ xcsv_wr_deinit()
   xcsv_style = nullptr;
 }
 
-static void
-xcsv_wr_position_deinit()
+void
+XcsvFormat::wr_position_deinit()
 {
-  xcsv_wr_deinit();
+  wr_deinit();
 }
 
-static void
-xcsv_wr_position(Waypoint* wpt)
+void
+XcsvFormat::wr_position(Waypoint* wpt)
 {
   /* Tweak incoming name if we don't have a fix */
   switch (wpt->fix) {
@@ -2119,26 +2035,9 @@ xcsv_wr_position(Waypoint* wpt)
   }
 
   waypt_add(wpt);
-  xcsv_data_write();
+  write();
   waypt_del(wpt);
 
   xcsv_file->stream.flush();
 }
-
-ff_vecs_t xcsv_vecs = {
-  ff_type_internal,
-  FF_CAP_RW_WPT, /* This is a bit of a lie for now... */
-  xcsv_rd_init,
-  xcsv_wr_init,
-  xcsv_rd_deinit,
-  xcsv_wr_deinit,
-  xcsv_data_read,
-  xcsv_data_write,
-  nullptr,
-  &xcsv_args,
-  CET_CHARSET_UTF8, 0, /* conversion to utf8 for gmsd is handled by the stream, don't let csv_convert_strings convert gmsd data */
-  { nullptr, nullptr, nullptr, xcsv_wr_position_init, xcsv_wr_position, xcsv_wr_position_deinit },
-  nullptr
-
-};
 #endif //CSVFMTS_ENABLED
diff --git a/xcsv.h b/xcsv.h
index 80b089db50fab968c67380f87374e28adabfc8a8..07885b4a536a66f212b7575bd5851806d5fe7eb4 100644 (file)
--- a/xcsv.h
+++ b/xcsv.h
 #ifndef XCSV_H_INCLUDED_
 #define XCSV_H_INCLUDED_
 
+#include <ctime>
 #include <utility>                // for move
 
 #include <QtCore/QByteArray>      // for QByteArray
+#include <QtCore/QDateTime>       // for QDateTime
 #include <QtCore/QList>           // for QList
 #include <QtCore/QString>         // for QString
 #include <QtCore/QStringList>     // for QStringList
+#include <QtCore/QVector>         // for QVector
 
 #include "defs.h"
+#include "format.h"
+#include "garmin_fs.h"
+#include "src/core/datetime.h"    // for DateTime
 #include "src/core/optional.h"    // for optional
 #include "src/core/textstream.h"  // for TextStream
 
 #if CSVFMTS_ENABLED
 
-/****************************************************************************/
-/* types required for various xcsv functions                                */
-/****************************************************************************/
+/*
+ * Class describing an xcsv format.
+ */
 
-class XcsvFile {
+class XcsvStyle
+{
 public:
-  XcsvFile() : mkshort_handle(mkshort_new_handle()) {}
-  // delete copy and move constructors and assignment operators.
-  // The defaults are not appropriate, and we haven't implemented proper ones.
-  XcsvFile(const XcsvFile&) = delete;
-  XcsvFile& operator=(const XcsvFile&) = delete;
-  XcsvFile(XcsvFile&&) = delete;
-  XcsvFile& operator=(XcsvFile&&) = delete;
-  ~XcsvFile() {
-    if (mkshort_handle != nullptr) {
-      mkshort_del_handle(&mkshort_handle);
-    }
-  }
+  /* Types */
 
-  gpsbabel::TextStream stream;
-  QString fname;
-  int gps_datum_idx{-1};               /* result of GPS_Lookup_Datum_Index */
-  short_handle mkshort_handle{nullptr};
-};
+  /* something to map fields to waypts */
+  struct field_map {
+    // We use QByteArrays because consumers want char* data and QByteArrays supply this through constData().
+    // If we used QStrings, then we would have to convert to QByteArrays to get the char* data.
+    // If we use char* then we have to manage memory allocation/deallocation.
+    // TODO: when consumers use QStrings then we can store QStrings instead of QByteArrays.
+    QByteArray key;
+    QByteArray val;
+    QByteArray printfc;
+    int hashed_key{0};
+    unsigned options{0};
 
-/* something to map fields to waypts */
-constexpr unsigned options_nodelim = 1;
-constexpr unsigned options_absolute = 2;
-constexpr unsigned options_optional = 4;
+    field_map() = default;
+    field_map(QByteArray k, QByteArray v, QByteArray p, int hk) : key{std::move(k)},val{std::move(v)},printfc{std::move(p)},hashed_key{hk} {}
+    field_map(QByteArray k, QByteArray v, QByteArray p, int hk, unsigned o) : key{std::move(k)},val{std::move(v)},printfc{
+      std::move(p)},hashed_key{hk},options{o} {}
+  };
 
-struct field_map {
-public:
-  // We use QByteArrays because consumers want char* data and QByteArrays supply this through constData().
-  // If we used QStrings, then we would have to convert to QByteArrays to get the char* data.
-  // If we use char* then we have to manage memory allocation/deallocation.
-  // TODO: when consumers use QStrings then we can store QStrings instead of QByteArrays.
-  QByteArray key;
-  QByteArray val;
-  QByteArray printfc;
-  int hashed_key{0};
-  unsigned options{0};
-
-  field_map() = default;
-  field_map(QByteArray k, QByteArray v, QByteArray p, int hk) : key{std::move(k)},val{std::move(v)},printfc{std::move(p)},hashed_key{hk} {}
-  field_map(QByteArray k, QByteArray v, QByteArray p, int hk, unsigned o) : key{std::move(k)},val{std::move(v)},printfc{
-          std::move(p)},hashed_key{hk},options{o} {}
-};
+  /* Constants */
+
+  static constexpr unsigned options_nodelim = 1;
+  static constexpr unsigned options_absolute = 2;
+  static constexpr unsigned options_optional = 4;
+
+  /* Member Functions */
+
+  static QString xcsv_get_char_from_constant_table(const QString& key);
+  static XcsvStyle xcsv_read_internal_style(const char* style_buf);
+  static XcsvStyle xcsv_read_style(const char* fname);
+
+  /* Data Members */
 
-/*
- * Class describing an xcsv format.
- */
-struct XcsvStyle {
   /* PROLOGUE from style file */
   /* header lines for writing at the top of the file. */
   QStringList prologue;
@@ -146,12 +141,214 @@ struct XcsvStyle {
 
   /* SHORTWHITE from style file */
   gpsbabel_optional::optional<int> whitespace_ok;
+
+private:
+  /* Types */
+
+  /* something to map config file constants to chars */
+  struct char_map_t {
+    const QString key;
+    const QString chars;
+  };
+
+  /* Member Functions */
+
+  static QString dequote(const QString& in);
+  static void validate_fieldmap(const field_map& fmp, bool is_output);
+  static void xcsv_ifield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc);
+  static void xcsv_ofield_add(XcsvStyle* style, const QString& qkey, const QString& qval, const QString& qpfc, unsigned int options);
+  static void xcsv_parse_style_line(XcsvStyle* style, QString line);
+  static XcsvStyle xcsv_parse_style_buff(const char* sbuff);
+
+  /* Data Members */
+
+  /* a table of config file constants mapped to chars */
+  static const char_map_t xcsv_char_table[];
 };
 
-/* public function prototypes */
+class XcsvFormat : public Format
+{
+public:
+  /* Member Functions */
+  QVector<arglist_t>* get_args() override
+  {
+    return &xcsv_args;
+  }
+
+  ff_type get_type() const override
+  {
+    return ff_type_internal;
+  }
+
+  QVector<ff_cap> get_cap() const override
+  {
+    return FF_CAP_RW_WPT; /* This is a bit of a lie for now... */
+  }
+
+  QString get_encode() const override
+  {
+    return CET_CHARSET_UTF8;
+  }
+
+  int get_fixed_encode() const override
+  {
+    return 0;
+  }
 
-void xcsv_setup_internal_style(const char* style_buf);
-XcsvStyle xcsv_read_internal_style(const char* style_buf);
+  void rd_init(const QString& fname) override;
+  void read() override;
+  void rd_deinit() override;
+  void wr_init(const QString& fname) override;
+  void write() override;
+  void wr_deinit() override;
+  void wr_position_init(const QString& fname) override;
+  void wr_position(Waypoint* wpt) override;
+  void wr_position_deinit() override;
+
+  void xcsv_setup_internal_style(const char* style_buf);
+
+private:
+  /* Types */
+
+  class XcsvFile
+  {
+  public:
+    /* Special Member Functions */
+
+    XcsvFile() : mkshort_handle(mkshort_new_handle()) {}
+    // delete copy and move constructors and assignment operators.
+    // The defaults are not appropriate, and we haven't implemented proper ones.
+    XcsvFile(const XcsvFile&) = delete;
+    XcsvFile& operator=(const XcsvFile&) = delete;
+    XcsvFile(XcsvFile&&) = delete;
+    XcsvFile& operator=(XcsvFile&&) = delete;
+    ~XcsvFile()
+    {
+      if (mkshort_handle != nullptr) {
+        mkshort_del_handle(&mkshort_handle);
+      }
+    }
+
+    /* Data Members */
+
+    gpsbabel::TextStream stream;
+    QString fname;
+    int gps_datum_idx{-1};             /* result of GPS_Lookup_Datum_Index */
+    short_handle mkshort_handle{nullptr};
+  };
+
+  struct xcsv_parse_data {
+    QString rte_name;
+    QString trk_name;
+    bool new_track{false};
+    double utm_northing{0};
+    double utm_easting{0};
+    double utm_zone{0};
+    char utm_zonec{'N'};
+    UrlLink* link_{nullptr};
+    gpsbabel_optional::optional<bool> lat_dir_positive;
+    gpsbabel_optional::optional<bool> lon_dir_positive;
+  };
+
+  /* Constants */
+
+  static constexpr char lat_dir(double a)
+  {
+    return a < 0.0 ? 'S' : 'N';
+  }
+  static constexpr char lon_dir(double a)
+  {
+    return a < 0.0 ? 'W' : 'E';
+  }
+
+  /* convert excel time (days since 1900) to time_t and back again */
+  static constexpr double excel_to_timet(double a)
+  {
+    return (a - 25569.0) * 86400.0;
+  }
+  static constexpr double timet_to_excel(double a)
+  {
+    return (a / 86400.0) + 25569.0;
+  }
+
+  static constexpr int gps_datum_wgs84 = 118; // GPS_Lookup_Datum_Index("WGS 84")
+
+  /* Member Functions */
+
+  static QDateTime yyyymmdd_to_time(const char* s);
+  static time_t sscanftime(const char* s, const char* format, int gmt);
+  static time_t addhms(const char* s, const char* format);
+  static QString writetime(const char* format, time_t t, bool gmt);
+  static QString writetime(const char* format, const gpsbabel::DateTime& t, bool gmt);
+  static QString writehms(const char* format, time_t t, int gmt);
+  static QString writehms(const char* format, const gpsbabel::DateTime& t, int gmt);
+  static long int time_to_yyyymmdd(const QDateTime& t);
+  static garmin_fs_t* gmsd_init(Waypoint* wpt);
+  static void xcsv_parse_val(const QString& value, Waypoint* wpt, const XcsvStyle::field_map& fmp, xcsv_parse_data* parse_data, int line_no);
+  void xcsv_resetpathlen(const route_head* head);
+  void xcsv_waypt_pr(const Waypoint* wpt);
+  QString xcsv_replace_tokens(const QString& original) const;
+
+  /* Data Members */
+
+  XcsvFile* xcsv_file{nullptr};
+  const XcsvStyle* xcsv_style{nullptr};
+  double pathdist = 0;
+  double oldlon = 999;
+  double oldlat = 999;
+
+  int waypt_out_count = 0;
+  const route_head* csv_track = nullptr;
+  const route_head* csv_route = nullptr;
+
+  char* styleopt = nullptr;
+  char* snlenopt = nullptr;
+  char* snwhiteopt = nullptr;
+  char* snupperopt = nullptr;
+  char* snuniqueopt = nullptr;
+  char* prefer_shortnames = nullptr;
+  char* xcsv_urlbase = nullptr;
+  char* opt_datum = nullptr;
+
+  const char* intstylebuf = nullptr;
+
+  QVector<arglist_t> xcsv_args = {
+    {
+      "style", &styleopt, "Full path to XCSV style file", nullptr,
+      ARGTYPE_FILE | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
+    },
+    {
+      "snlen", &snlenopt, "Max synthesized shortname length", nullptr,
+      ARGTYPE_INT, "1", nullptr, nullptr
+    },
+    {
+      "snwhite", &snwhiteopt, "Allow whitespace synth. shortnames",
+      nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+    },
+    {
+      "snupper", &snupperopt, "UPPERCASE synth. shortnames",
+      nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+    },
+    {
+      "snunique", &snuniqueopt, "Make synth. shortnames unique",
+      nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+    },
+    {
+      "urlbase", &xcsv_urlbase, "Basename prepended to URL on output",
+      nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+    },
+    {
+      "prefer_shortnames", &prefer_shortnames,
+      "Use shortname instead of description",
+      nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+    },
+    {
+      "datum", &opt_datum, "GPS datum (def. WGS 84)",
+      nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+    },
+  };
+
+};
 
 #endif // CSVFMTS_ENABLED
 #endif // XCSV_H_INCLUDED_